---
title: "With Terraform"
---

In this tutorial, you will set up Digger to automate terraform pull requests using Github Actions

**Prerequisites**
- A GitHub repository with valid terraform code. Don't have one? see [Example repo](https://github.com/diggerhq/quickstart-actions-aws)
- Your cloud provider credentials:
  - For AWS: [Hashicorp's AWS tutorial](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build)
  - For GCP: [Hashicorp's GCP tutorial](https://developer.hashicorp.com/terraform/tutorials/gcp-get-started/google-cloud-platform-build)
  - For Azure: [Hashicorp's Azure tutorial](https://developer.hashicorp.com/terraform/tutorials/azure-get-started/azure-build)

<Steps>
  <Step title="Create your Digger account">

Head to [otaco.app](https://otaco.app) and sign up using your preferred method.

Post sign up, your dashboard should look like this:

![](/images/readme/ui.digger.dev.png) 


  </Step>

  <Step title="Install the Digger GitHub App">

Install the Digger [GitHub App](https://github.com/apps/digger-pro/installations/select_target) into your repository.

<Note>
Digger GitHub App does not need access to your cloud account, it just starts jobs in your CI. All sensitive data stays in your CI job.

You can also [self-host Digger orchestrator](/ce/self-host/deploy-docker) with a private GiHub app and issue your own token

</Note>
  </Step>

  <Step title="Create Action Secrets with cloud credentials">

In GitHub repository settings, go to Secrets and Variables - Actions. Create the following secrets:

<Tabs>
  <Tab title="AWS">
    - `AWS_ACCESS_KEY_ID` - `AWS_SECRET_ACCESS_KEY` You can also [use
    OIDC](/ce/cloud-providers/authenticating-with-oidc-on-aws) for AWS
    authentication.
    
    <Accordion title="Tip: Set GitHub Action secrets with gh CLI">
      From the repository root (with GitHub CLI installed):
      
      ```bash
      # Set AWS credentials as repository Action secrets
      gh secret set AWS_ACCESS_KEY_ID --body "$AWS_ACCESS_KEY_ID"
      gh secret set AWS_SECRET_ACCESS_KEY --body "$AWS_SECRET_ACCESS_KEY"
      ```
    </Accordion>
  </Tab>
  <Tab title="GCP">
    - `GCP_CREDENTIALS` - contents of your GCP Service Account Key json file You
    can also [use OIDC](/gcp/federated-oidc-access/) for GCP authentication.
    
    <Accordion title="Tip: Set GitHub Action secrets with gh CLI">
      If your Service Account key is saved to a file, you can pipe it directly:
      
      ```bash
      # Set GCP credentials secret from a JSON key file
      gh secret set GCP_CREDENTIALS < path/to/service-account-key.json
      ```
      
      Or set from an environment variable/string:
      
      ```bash
      gh secret set GCP_CREDENTIALS --body "$(cat path/to/service-account-key.json)"
      ```
    </Accordion>
  </Tab>
  <Tab title="Azure">
    - `AZURE_CLIENT_ID` - Your Azure App Registration Client ID
    - `AZURE_TENANT_ID` - Your Azure Tenant ID
    - `AZURE_SUBSCRIPTION_ID` - Your Azure Subscription ID
    
    You'll need to configure OIDC authentication by setting up federated credentials in your Azure App Registration. See [Azure OIDC setup](/ce/azure-specific/azure) for details.
    
    <Accordion title="Tip: Set GitHub Action secrets with gh CLI">
      From the repository root (with GitHub CLI installed):
      
      ```bash
      gh secret set AZURE_CLIENT_ID --body "$AZURE_CLIENT_ID"
      gh secret set AZURE_TENANT_ID --body "$AZURE_TENANT_ID"
      gh secret set AZURE_SUBSCRIPTION_ID --body "$AZURE_SUBSCRIPTION_ID"
      ```
    </Accordion>
  </Tab>
</Tabs>
  </Step>

  <Step title="Create digger.yml">

This file contains Digger configuration and needs to be placed at the root level of your repository. Assuming your terraform code is in the `prod` directory:

```
projects:
- name: production
  dir: prod
```
  </Step>

  <Step title="Create Github Actions workflow file">

Place it at `.github/workflows/digger_workflow.yml` (name is important!)

<Tabs>
  <Tab title="AWS">
    ```yaml
    name: Digger Workflow

    on:
      workflow_dispatch:
        inputs:
          spec:
            required: true
          run_name:
            required: false

    run-name: '${{inputs.run_name}}'

    jobs:
      digger-job:
        runs-on: ubuntu-latest
        permissions:
          contents: write      # required to merge PRs
          actions: write       # required for plan persistence
          id-token: write      # required for workload-identity-federation
          pull-requests: write # required to post PR comments
          issues: read         # required to check if PR number is an issue or not
          statuses: write      # required to validate combined PR status

        steps:
          - uses: actions/checkout@v4
          - name: ${{ fromJSON(github.event.inputs.spec).job_id }}
            run: echo "job id ${{ fromJSON(github.event.inputs.spec).job_id }}"
          - uses: diggerhq/digger@vLatest
            with:
              digger-spec: ${{ inputs.spec }}
              setup-aws: true
              setup-terraform: true
              terraform-version: 1.5.5
              aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
              aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            env:
              GITHUB_CONTEXT: ${{ toJson(github) }}
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    ```

  </Tab>
  <Tab title="GCP">
    ```yaml
    name: Digger

    on:
      workflow_dispatch:
        inputs:
          spec:
            required: true
          run_name:
            required: false

    run-name: '${{inputs.run_name}}'

    jobs:
      digger-job:
        name: Digger
        runs-on: ubuntu-latest
        permissions:
          contents: write      # required to merge PRs
          actions: write       # required for plan persistence
          id-token: write      # required for workload-identity-federation
          pull-requests: write # required to post PR comments
          issues: read         # required to check if PR number is an issue or not
          statuses: write      # required to validate combined PR status
        steps:
        - uses: actions/checkout@v4
        - name: ${{ fromJSON(github.event.inputs.spec).job_id }}
          run: echo "job id ${{ fromJSON(github.event.inputs.spec).job_id }}"
        - id: 'auth'
          uses: 'google-github-actions/auth@v1'
          with:
            credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
            create_credentials_file: true
        - name: 'Set up Cloud SDK'
          uses: 'google-github-actions/setup-gcloud@v1'
        - name: 'Use gcloud CLI'
          run: 'gcloud info'
        - name: digger run
            uses: diggerhq/digger@vLatest
            with:
              digger-spec: ${{ inputs.spec }}
              setup-aws: false
              setup-terraform: true
              terraform-version: 1.5.5
            env:
              GITHUB_CONTEXT: ${{ toJson(github) }}
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    ```

    This workflow includes additional steps for GCP:
    - Authenticate into GCP using Google's official [Auth action](https://github.com/google-github-actions/auth). Note the `create_credentials_file: true` option; without it, subsequent steps that rely on [Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc) will not work.
    - Set up Google Cloud SDK for use in the subsequent steps via Google's official [Setup-gcloud action](https://github.com/google-github-actions/setup-gcloud)
    - Verify that GCP is configured correctly by running `gcloud info`

  </Tab>
  <Tab title="Azure">
    ```yaml
    name: Digger Workflow

    on:
      workflow_dispatch:
        inputs:
          spec:
            required: true
          run_name:
            required: false

    run-name: '${{inputs.run_name}}'

    jobs:
      digger-job:
        runs-on: ubuntu-latest
        permissions:
          contents: write      # required to merge PRs
          actions: write       # required for plan persistence
          id-token: write      # required for workload-identity-federation
          pull-requests: write # required to post PR comments
          issues: read         # required to check if PR number is an issue or not
          statuses: write      # required to validate combined PR status

        steps:
          - uses: actions/checkout@v4
          - name: ${{ fromJSON(github.event.inputs.spec).job_id }}
            run: echo "job id ${{ fromJSON(github.event.inputs.spec).job_id }}"
          - uses: diggerhq/digger@vLatest
            with:
              digger-spec: ${{ inputs.spec }}
              setup-azure: true
              azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
              azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
              azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
              setup-terraform: true
              terraform-version: 1.5.5
            env:
              GITHUB_CONTEXT: ${{ toJson(github) }}
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
              ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
              ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    ```
    
    This workflow uses Azure OIDC authentication, which requires:
    - Setting up federated credentials in your Azure App Registration for GitHub Actions
    - The `id-token: write` permission for workload identity federation
    - ARM_* environment variables for the Azure Terraform provider
  </Tab>
</Tabs>
  </Step>

  <Step title="Create a PR to verify that it works">

Terraform will run an existing plan against your code.

Make any change to your terraform code e.g. add a blank line. An action run should start (you can see log output in Actions). After some time you should see output of Terraform Plan added as a comment to your PR.


<Info>If you forked one of the demo repositories you will need to enable Actions in your repository.</Info>
Then you can add a comment like `digger apply` and shortly after apply output will be added as comment too.

<video
  autoPlay
  muted
  loop
  playsInline
  className="w-full aspect-video rounded-xl"
  src="/videos/apply.mp4"
></video>
</Step>
</Steps>


